/* bredeche(at)lri.fr
 * Created on 14 d�c. 2006
 */

package piconode.factory;

import java.util.ArrayList;

import piconode.core.arc.WeightedArc;
import piconode.core.node.RecurrentNeuralNetworkWithBias;
import piconode.core.node.RecurrentNeuron;
import piconode.ext.ActivationFunction_HyperbolicTangent;
import piconode.ext.ActivationFunction_Linear;
import piconode.ext.ActivationFunction_LogisticSigmoid;
import piconode.toolbox.Tools;
import piconode.visualpiconode.Visualizer;

public class ElmanNetworkFactory {

	/**
	 * build a simple Elman NN with bias neuron
	 * 
	 * @param __nbin
	 * @param __nbhidden
	 * @param __nbout
	 * @param __activationFunction
	 * @return
	 */
	public static RecurrentNeuralNetworkWithBias createElmanNet(int __nbin, int __nbhidden, int __nbout, boolean __activationFunction) {
		return ElmanNetworkFactory.createElmanNet(__nbin, __nbhidden, __nbout, __activationFunction, true);
	}

	/**
	 * build a simple Elman NN (i.e. each hidden node is connected to all nodes
	 * in the hidden layer, including self-connection)
	 * 
	 * @param __nbin
	 * @param __nbhidden
	 * @param __nbout
	 * @param __activationFunction
	 *            true is logistic sigmoid (output: 0;1), false if tanh (output:
	 *            -1;1)
	 * @param __biasNeuron
	 * @return
	 */
	public static RecurrentNeuralNetworkWithBias createElmanNet(int __nbin, int __nbhidden, int __nbout, boolean __activationFunction, boolean __biasNeuron) {
		if (__nbhidden == 0) {
			System.out.println("[ERROR] ElmanNetworkFactory.createElmanNet(...) - cannot create a network with zero hidden node.");
			System.exit(-1);
		}

		// step 1 : create a network

		RecurrentNeuralNetworkWithBias network;
		network = new RecurrentNeuralNetworkWithBias(__biasNeuron);

		// step 2 & 3 : create some neurons and register inputs/outputs

		ArrayList inputList = new ArrayList();
		for (int i = 0; i != __nbin; i++) {
			RecurrentNeuron neuron;
			neuron = new RecurrentNeuron(network, new ActivationFunction_Linear(), "in(" + i + ")");
			inputList.add(neuron);
			network.registerInputNeuron(neuron);
		}
		ArrayList hiddenList = new ArrayList();
		for (int i = 0; i != __nbhidden; i++) {
			if (__activationFunction == true)
				hiddenList.add(new RecurrentNeuron(network, new ActivationFunction_LogisticSigmoid(), "hidden(" + i + ")"));
			else
				hiddenList.add(new RecurrentNeuron(network, new ActivationFunction_HyperbolicTangent(), "hidden(" + i + ")"));
		}
		ArrayList outputList = new ArrayList();
		for (int i = 0; i != __nbout; i++) {
			// NeuronForBackPropLearning neuron = new NeuronForBackPropLearning(
			// network, "out("+i+")");
			RecurrentNeuron neuron;
			neuron = new RecurrentNeuron(network, new ActivationFunction_Linear(), "out(" + i + ")");
			outputList.add(neuron);
			network.registerOutputNeuron(neuron);
		}

		// step 4 : create the topology

		for (int i = 0; i != inputList.size(); i++)
			for (int j = 0; j != hiddenList.size(); j++)
				network.registerArc(new WeightedArc((RecurrentNeuron) inputList.get(i), (RecurrentNeuron) hiddenList.get(j), Tools.getArcWeightRandomInitValue(-1, 2)));
		for (int i = 0; i != hiddenList.size(); i++) {
			for (int j = 0; j != hiddenList.size(); j++)
				// link to all other hidden nodes, including self-connections
				// if ( i != j ) // if no self-connect
				network.registerArc(new WeightedArc((RecurrentNeuron) hiddenList.get(i), (RecurrentNeuron) hiddenList.get(j), Tools.getArcWeightRandomInitValue(-1, 2)));
			for (int j = 0; j != outputList.size(); j++)
				// link to outputs
				network.registerArc(new WeightedArc((RecurrentNeuron) hiddenList.get(i), (RecurrentNeuron) outputList.get(j), Tools.getArcWeightRandomInitValue(-1, 2)));
		}

		// step 5 : initialize the network (perform some integrity checks and
		// internal encoding)

		network.initNetwork();

		// step 6 (optional) : set parameters for learning -- here we use
		// default parameters (all nodes are learning nodes + etalearningrate is
		// 1)

		// none

		// end of init

		return network;
	}

	// testing.
	public static void main(String[] args) {
		RecurrentNeuralNetworkWithBias net = createElmanNet(4, 6, 2, true);
		net.displayInformation();
		int retour = Visualizer.showNetwork(net);
	}

}
